跳到主要内容

Python 执行文件加密

打包成 exe

依赖文件怎么打包? 不需要 Python 的执行环境,只需要一个独立的可执行文件,这样就不需要安装 Python 环境了。

常见的源码保护手段

  • 发行 .pyc 文件
  • 代码混淆
  • 使用 py2exe
  • 使用 Cython

发行 .pyc 文件

Python 解释器在执行代码的过程中会首先生成 .pyc 文件,然后解释执行 .pyc 文件中的内容。当然了,Python 解释器也能够直接执行 .pyc 文件。而 .pyc 文件是二进制文件,无法直接看出源码内容。如果发行代码到客户环境时都是 .pyc 而非 .py 文件的话,那岂不是能达到保护 Python 代码的目的?

把 .py 文件编译为 .pyc 文件,是件非常轻松地事情,可不需要把所有代码跑一遍,然后去捞生成的 .pyc 文件。

事实上,Python 标准库中提供了一个名为 compileall 的库,可以轻松地进行编译。

执行如下命令能够将遍历 <src> 目录下的所有 .py 文件,将之编译为 .pyc 文件:

python -m compileall <src> 

然后删除 <src> 目录下所有 .py 文件就可以打包发布了:

$ find <src> -name '*.py' -type f -print -exec rm {} \;

.pyc 文件是什么

.pyc 文件是 Python 编译过的字节码文件。当你运行一个 Python 程序时,Python 解释器首先将源代码(.py 文件)编译成字节码(Bytecode),这是一种低级的、平台无关的代码表示,然后这个字节码会被 Python 的虚拟机执行。这个编译过程的目的是优化程序的执行速度,因为直接执行字节码比每次都从源代码解释执行要快。

.pyc 文件通常存储在 __pycache__目录下,文件名会包含 Python 版本号和优化级别的信息,例如 your_script.cpython-38.pyc 表示文件是用 Python 3.8 版本编译的。

为什么使用 .pyc 文件?

  • 执行速度:使用 .pyc 文件可以加快 Python 程序的启动时间,因为解释器可以直接加载已经编译好的字节码到虚拟机执行,避免了重复的编译过程。
  • 减少CPU使用:编译过程只在源代码第一次执行时进行,之后只要源代码没有变化,就可以直接使用 .pyc 文件,这样可以减少 CPU 的使用。
  • 分发字节码:虽然 .pyc 文件不是为了隐藏或加密源代码(因为字节码相对容易被反编译),但它们可以用于分发不希望直接暴露源代码的 Python 程序。

限制和注意事项

  • 平台无关性:虽然字节码是平台无关的,但 .pyc 文件依赖于特定版本的 Python 解释器。不同版本的 Python 可能无法正确地解释其他版本编译的字节码。
  • 安全性.pyc 文件不提供真正的代码保护或加密。有兴趣的人可以使用反编译工具查看或恢复源代码。
  • 管理:在某些情况下,.pyc 文件可能需要手动管理,特别是在清理旧的编译文件或解决版本冲突时。

优点

  • 简单方便,提高了一点源码破解门槛
  • 平台兼容性好,.py 能在哪里运行,.pyc 就能在哪里运行

不足

  • 解释器兼容性差,.pyc 只能在特定版本的解释器上运行
  • 有现成的反编译工具,破解成本低

python-uncompyle6 就是这样一款反编译工具,效果出众。

执行如下命令,即可将 .pyc 文件反编译为 .py 文件:

$ uncompyle6 *compiled-python-file-pyc-or-pyo*

使用 py2exe

py2exe 是一款将 Python 脚本转换为 Windows 平台上的可执行文件的工具。其原理是将源码编译为 .pyc 文件,加之必要的依赖文件,一起打包成一个可执行文件。

如果最终发行由 py2exe 打包出的二进制文件,那岂不是达到了保护源码的目的?

使用 py2exe 进行打包的步骤较为简便。

1)编写入口文件。本示例中取名为 hello.py:

print 'Hello World'

2)编写 setup.py:

from distutils.core import setup
import py2exe

setup(console=['hello.py'])

生成可执行文件

python setup.py py2exe

优点

  • 能够直接打包成 exe,方便分发和执行
  • 破解门槛比 .pyc 更高一些

不足

兼容性差,只能运行在 Windows 系统上 生成的可执行文件内的布局是明确、公开的,可以找到源码对应的 .pyc 文件,进而反编译出源码

使用 Cython

虽说 Cython 的主要目的是带来性能的提升,但是基于它的原理:将 .py/.pyx 编译为 .c 文件,再将 .c 文件编译为 .so(Unix).pyd(Windows),其带来的另一个好处就是难以破解。

使用 Cython 进行开发的步骤也不复杂。

1)编写文件 hello.pyx 或 hello.py:

def hello():
print('hello')

2)编写 setup.py:

from distutils.core import setup
from Cython.Build import cythonize

setup(name='Hello World app',
ext_modules=cythonize('hello.pyx'))

3)编译为 .c,再进一步编译为 .so.pyd

python setup.py build_ext --inplace

执行 python -c "from hello import hello;hello()" 即可直接引用生成的二进制文件中的 hello() 函数。

优点

  • 生成的二进制 .so 或 .pyd 文件难以破解
  • 同时带来了性能提升

不足

  • 兼容性稍差,对于不同版本的操作系统,可能需要重新编译
  • 虽然支持大多数 Python 代码,但如果一旦发现部分代码不支持,完善成本较高

代码混淆方案

可以使用 pyarmor 进行代码混淆,将源码中的变量名、函数名等进行混淆,增加破解的难度。

不过这个方案需要钱